package xijia.plus.docs.thymeleafuser.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ZipUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import io.github.wslxm.springbootplus2.common.auth.util.JwtUtil;
import io.github.wslxm.springbootplus2.core.config.threadpool.XjThreadUtil;
import io.github.wslxm.springbootplus2.manage.sys.service.MsgService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import xijia.plus.docs.enums.Yw;
import xijia.plus.docs.manage.all.model.vo.DocInfoClientVO;
import xijia.plus.docs.manage.all.model.vo.DocInfoVO;
import xijia.plus.docs.manage.all.model.vo.DocMenuVO;
import xijia.plus.docs.manage.all.service.DocInfoService;
import xijia.plus.docs.thymeleafuser.constant.TpParamsKey;
import xijia.plus.docs.thymeleafuser.service.PageService;
import xijia.plus.docs.thymeleafuser.service.TpService;
import xijia.plus.docs.thymeleafuser.util.ExportMdUtil;
import xijia.plus.docs.thymeleafuser.util.IpUtil;
import xijia.plus.docs.thymeleafuser.util.TpUtil;
import xijia.plus.docs.thymeleafuser.util.XjFileUtil;

import java.io.File;
import java.io.InputStream;
import java.nio.file.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
@Slf4j
public class TpServiceImpl extends BaseServiceImpl implements TpService {


    @Autowired
    private TpUtil tpUtil;

    @Autowired
    private DocInfoService docInfoService;
    @Autowired
    private PageService pageService;

    @Autowired
    private MsgService msgService;

    /**
     * 静态化后保存地址
     */
    private final static String EXPORT_PATH = "dist";

    @Override
    public void siteStatic() {
        // 删除上一次数据
        File file = new File(EXPORT_PATH);
        FileUtil.clean(file);
        String userId = JwtUtil.getJwtUser(request).getUserId();
        msgService.sendSysMsg(userId, "正在进行异步处理文档静态化HTML, 请等待完成通知, 文档数量越多, 耗时越长");
        JSONObject docConfig = JSON.parseObject(super.getConfig("doc-config"));
        String seoHomeConfig = super.getConfig("seo-home");
        String seoDocConfig = super.getConfig("seo-doc");
        //  List<DocMenuVO> trees = super.getTree();
        Map<Integer, List<DocInfoClientVO>> docTree = super.getDocTree();
        String baseUrl = request.getRequestURL().toString().replace(request.getServletPath(), "");
        String ipAddr = IpUtil.getIpAddr(request);
        String tenantName = getTenantName();
        List<DocMenuVO> homeMenus = super.getTree();
        List<DocMenuVO> docMenus = super.getTree();
        XjThreadUtil.asyncExecute(() -> {
            // 生成html
            this.homeStatic(homeMenus, docTree, docConfig, seoHomeConfig, baseUrl, ipAddr);
            this.docStatic(docMenus, docTree, docConfig, seoDocConfig, baseUrl, ipAddr, tenantName);
            log.info("已导出 --> / 所有文档");

            // 静态资源文件夹 static/下的 static 目录  拷贝到 dist/ 目录下
            XjFileUtil.mkdirFile("File/static");
            FileUtil.copy(new File("File/static"), new File(EXPORT_PATH), true);
            // 此代码只在本地时有校
            // ResourceCopyUtil.copyResources("static/static", EXPORT_PATH);

            log.info("已导出 --> / 相关 css/js 静态资源");

            // 打压缩包
            File zip = ZipUtil.zip(new File("File/dist.zip"), false, new File(EXPORT_PATH));
            log.info("已压缩 --> File/dist.zip");

            msgService.sendSysMsg(userId, "文档静态化HTML完成, 请自行下载数据");
        });
    }


    /**
     * 首页静态化
     *
     * @return void
     * @author wangsong
     */
    public void homeStatic(List<DocMenuVO> trees,
                           Map<Integer, List<DocInfoClientVO>> docTree,
                           JSONObject docConfig,
                           String seoHomeConfig,
                           String baseUrl,
                           String ipAddr) {

        Map<String, Object> params = new HashMap<>();
        this.menuNext(trees, false);
        // 采用相对路径获取静态资源 (./ 使用当前目录下)
        params.put(TpParamsKey.PAGE, "index");
        params.put(TpParamsKey.STATIC_PATH, ".");
        params.put(TpParamsKey.ADDR_ID, ipAddr);
        params.put(TpParamsKey.BASE_URL, baseUrl);
        params.put(TpParamsKey.MENU_TOP, trees.get(0).getChildren());
        params.put(TpParamsKey.DOCS_TREE, JSON.parse(JSON.toJSONString(docTree)));
        params.put(TpParamsKey.SEO, seoHomeConfig);
        params.put(TpParamsKey.CONFIG, docConfig);
        // 生成静态html
        String htmlPath = tpUtil.createHtml("index", EXPORT_PATH + "/index", BeanUtil.beanToMap(params));
        log.info("已导出 --> /" + htmlPath);
    }


    /**
     * 文档静态化
     *
     * @return void
     * @author wangsong
     */
    public void docStatic(List<DocMenuVO> trees, Map<Integer,
            List<DocInfoClientVO>> docTree,
                          JSONObject docConfig,
                          String seoDocConfig,
                          String baseUrl,
                          String ipAddr
            , String tenantName
    ) {
        // ====== 通用数据 start ==========
        this.menuNext(trees, true);
        Map<String, Object> params = new HashMap<>();
        // 这里文档生成到二级目录,所以静态资源 (../ 退一层的static 目录下)
        params.put(TpParamsKey.PAGE, "doc");
        params.put(TpParamsKey.STATIC_PATH, "..");
        params.put(TpParamsKey.ADDR_ID, ipAddr);
        params.put(TpParamsKey.BASE_URL, baseUrl);
        params.put(TpParamsKey.MENU_TOP, trees.get(0).getChildren());
        // params.put(TpParamsKey.MENU_BOTTOM, menuMap.get(2).get(0).getMenus());
        params.put(TpParamsKey.DOCS_TREE, JSON.parse(JSON.toJSONString(docTree)));
        params.put(TpParamsKey.CONFIG, docConfig);
        // ====== 通用数据 end  ==========
        for (List<DocInfoClientVO> docs : docTree.values()) {
            this.doctNext(docs, params, tenantName, seoDocConfig);
        }
    }


    private void doctNext(List<DocInfoClientVO> docs, Map<String, Object> params, String tenantName, String seoDocConfig) {

        if (docs == null || docs.size() == 0) {
            return;
        }
        for (DocInfoClientVO doc : docs) {
            // 判断是文档还是目录
            if (doc.getShowType().equals(Yw.DocShowType.V1.getValue())) {
                // 递归
                this.doctNext(doc.getChildren(), params, tenantName, seoDocConfig);
            } else {
                // 查询文档
                DocInfoVO docInfoVO = docInfoService.findByName(tenantName, doc.getTypeName(), doc.getName());
                if (StringUtils.isBlank(docInfoVO.getContentHtml())) {
                    docInfoVO.setContentHtml("暂无内容");
                }
                // 处理内容中的 {{}} 或者 {{{ }}}  {{{{ }}}}
                if (StringUtils.isNotBlank(docInfoVO.getContentHtml())) {
                    for (int i = 0; i < 3; i++) {
                        docInfoVO.setContentHtml(docInfoVO.getContentHtml().replaceAll("\\{\\{", "\\{ \\{"));
                        docInfoVO.setContentHtml(docInfoVO.getContentHtml().replaceAll("}}", "} }"));
                    }
                }
                // 处理内容中的图片
                String contentHtml = this.exportMdImgLocal("/static/images/", docInfoVO.getContent(), docInfoVO.getContentHtml(), docInfoVO.getName());
                docInfoVO.setContentHtml(contentHtml);

                params.put(TpParamsKey.DOC_INFO, docInfoVO);
                params.put(TpParamsKey.SEO, pageService.getSeoStr(docInfoVO, seoDocConfig));

                // 当前访问的别名返回（处理分页使用）
                String htmlPath = tpUtil.createHtml("doc/doc", EXPORT_PATH + "/" + docInfoVO.getTypeName() + "/" + docInfoVO.getName(), BeanUtil.beanToMap(params));
                log.info("已导出 --> /" + htmlPath);
            }
        }
    }


    /**
     * 图片解析 保存到本地
     * 临时保存到 target 目录中的 /static 目录下
     *
     * @param imgPath     保存路径  /static/images/
     * @param markdownStr 文档内容
     * @param htmlStr     文档内容
     * @param name        当前文档名
     * @author wangsong
     * @email 1720696548@qq.com
     * @date 2022/12/22 0022 19:34
     * @version 1.0.0
     */
    private String exportMdImgLocal(String imgPath, String markdownStr, String htmlStr, String name) {
        if (StringUtils.isBlank(markdownStr)) {
            return htmlStr;
        }
        List<String> imgUrls = ExportMdUtil.analysisGetMdImgUrls(markdownStr);
        for (String imgUrl : imgUrls) {
            // 获取文件
            String newImgUrl = ExportMdUtil.imgUrlClean(imgUrl);
            String fileName = newImgUrl.substring(newImgUrl.lastIndexOf("/") + 1, newImgUrl.length());
            InputStream inputStream = XjFileUtil.getInputStreamByUrl(newImgUrl, fileName);
            try {
                File basePath = new File(EXPORT_PATH);
                Path directory = Paths.get(basePath.getPath(), imgPath);
                if (!Files.exists(directory, new LinkOption[0])) {
                    Files.createDirectories(directory);
                }
                Files.copy(inputStream, directory.resolve(fileName), StandardCopyOption.REPLACE_EXISTING);
                log.info("图片导出 -->  文档:{}  -->  图片URL: {}  已下载保存到: {} 目录下, 文本内容已替换", name, imgUrl, imgPath);
                // 替换内容文本
                htmlStr = htmlStr.replace(imgUrl, ".." + imgPath + fileName);
            } catch (Exception e) {
                log.info("图片导出 -->  文档:{}  -->  图片URL地址: {}  | 替换失败  错误信息: {}", name, imgUrl, e.getMessage());
            }
        }
        return htmlStr;
    }


    /**
     * 修改菜单为 ../相对路径
     *
     * @param menus
     * @param isHierarchy 是否退层级
     */
    private void menuNext(List<DocMenuVO> menus, boolean isHierarchy) {
        if (menus == null || menus.size() == 0) {
            return;
        }
        for (DocMenuVO doc : menus) {
            this.menuNext(doc.getChildren(), isHierarchy);

            if (StringUtils.isNotBlank(doc.getUrl()) && doc.getUrl().substring(0, 1).equals("/")) {
                if (isHierarchy) {
                    doc.setUrl(".." + doc.getUrl());
                } else {
                    doc.setUrl("." + doc.getUrl());
                }
            }
        }
    }
}
